#!/usr/bin/env python
#coding=utf-8
from socket import *
from threading import *
import sys
import copy
import time

#本模块中使用客户的socket address来代表客户，相当于id

MAX_CLIENTS = 1000
clientCount = 0 #number of clients
waiting = [] #address
game = {} #Host address:Guest address，Host和Guest是键与键值的关系
num = {} #address:num，客户所选数字
lock = RLock()
gameCondition = {} #address:condition，针对一对在进行游戏的玩家的条件变量
guessCondition = {} #address:condition，针对一次猜测的条件变量
result = {} #address:result，一次猜测的结果
sockets = {} #address:socket，客户的socket

def startGame(address, myGameCondition):
    
    #引用全局变量
    global clientCount
    global waiting
    global game
    global lock
    global gameCondition
    isHost = False #是否为Host，此处Host表明是在等待对方加入游戏
    
    #修改了全局变量，使用lock
    lock.acquire()
    if len(waiting) == 0:
        #没有玩家在等待，于是被加入等待列表
        waiting.append(address)
        myGameCondition.acquire()
        isHost = True
    else:
        #与等待列表中的第一个玩家进行游戏
        hostAddr = waiting[0]
        game[hostAddr] = address #记录Host和Guest
        waiting.remove(hostAddr)
        isHost = False
        print address, 'Joined Game!'
        #通知对方可以开始游戏
        gameCondition[hostAddr].acquire()
        gameCondition[hostAddr].notify()
        gameCondition[hostAddr].release()
    print clientCount, waiting, game
    lock.release()
    
    if isHost: #使用条件变量等待对手加入游戏
        print address, 'is waiting for opponent'
        myGameCondition.wait()
        myGameCondition.release()
        print 'Opponent Joined in, Game Start!'

def quitGame(address):
    
    #引用全局变量
    global waiting
    global game
    global num
    global lock
    global guessCondition
    global result
    
    #修改了全局变量，使用lock
    lock.acquire()
    if address in waiting: #若玩家在等待列表中，直接移除
        waiting.remove(address)
    else:
        if address in game:
            #玩家作为Host
            guest = game[address] #获取对手address
            result[address] = -1 #将游戏结果设为无效值
            result[guest] = -1
            del game[address] #删除对应的游戏
            #解除对手等待状态
            guessCondition[guest].acquire()
            guessCondition[guest].notify()
            guessCondition[guest].release()
        else:
            #玩家作为Guest
            for host in game.keys():
                if cmp(game[host], address) == 0: #寻找对应的游戏
                    result[address] = -1 #将游戏结果设为无效值
                    result[host] = -1
                    del game[host] #删除对应的游戏
                    #解除对手等待状态
                    guessCondition[host].acquire()
                    guessCondition[host].notify()
                    guessCondition[host].release()
                    break
    num[address] = -1
    lock.release()
    
def connectThread(conn, addr):
    '''
    Function called by thread to deal with client connection
    '''

    #引用全局变量
    global clientCount
    global waiting
    global game
    global num
    global lock
    global gameCondition
    global guessCondition
    global result
    global sockets
    sock = copy.copy(conn)
    address = copy.copy(addr)
    sockets[address] = sock
    
    try:
        msg = sock.recv(1024)
        #print msg
    except:
        print '>>>A \'receive\' error happened! IP:', address[0], 'PORT:', address[1]
        sock.close()
        return

    #判断是否只是获取策略文件
    #每个客户端都先请求策略文件，然后断开，用新端口重连，因此此请求只可能出现在第一条信息中
    #为免在循环中重复进行无意义的判断，将判断代码提取出来
    try:
        if cmp(msg, '<policy-file-request/>\000') == 0:
            sock.send('''<?xml version="1.0"?><!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"><cross-domain-policy>  <site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/><allow-http-request-headers-from domain="*" headers="*"/></cross-domain-policy>'''+'\0')
            sock.close()
            return
        elif cmp(msg, '') == 0:
            sock.close()
            return
    except:
        print '>>>A \'send\' error happened! IP:', address[0], 'PORT:', address[1]
        sock.close()
        return
    
    myGameCondition = Condition() #针对一对在进行游戏的玩家的条件变量
    myGuessCondition = Condition() #针对一次猜测的条件变量
    
    #初始化参数
    #因为修改了全局变量，此处使用lock
    lock.acquire()
    clientCount += 1
    num[address] = -1
    result[address] = -1
    gameCondition[address] = myGameCondition
    guessCondition[address] = myGuessCondition
    lock.release()
    
    #对游戏双方进行初始化设置
    startGame(address, myGameCondition)
    
    try:
        sock.send('Game Start!')
    except:
        print '>>>A \'send\' error happened! IP:', address[0], 'PORT:', address[1]
    
    while(True):
        #接收玩家发送过来的消息
        try:
            msg = sock.recv(1024)
            #print address, msg
        except:
            print '>>>A \'receive\' error happened! IP:', address[0], 'PORT:', address[1]
            break
        
        #处理消息
        try:
            if cmp(msg, 'Start Playing!\n') == 0: #玩家想开始游戏
                #对游戏双方进行初始化设置
                startGame(address, myGameCondition)
                #通知玩家游戏开始
                try:
                    sock.send('Game Start!')
                except:
                    print '>>>A \'send\' error happened! IP:', address[0], 'PORT:', address[1]
                    break
                
            elif cmp(msg, 'Quit Playing!\n') == 0: #玩家退出游戏
                #退出游戏，并解除对手等待状态
                quitGame(address)
                print address, 'Quit Playing!'
                print clientCount, waiting, game

            elif cmp(msg, '') == 0: #页面关闭时msg为空
                break
            
            else:
                #获取玩家的选择
                lock.acquire()
                try:
                    msgs = msg.split(':')
                    num[address] = int(msgs[0]) #保存选择的数字
                except:
                    print 'Something Wrong!', address, msg
                    sock.send('Something Wrong!')
                    break
                #print address, num[address]
                lock.release()
                
                isWaiting = False
                lock.acquire()
                if address in game: #玩家是Host
                    #告诉对手自己的选择
                    guest = game[address]
                    if msgs[1] == '1\n':
                        sockets[guest].send('Opponent Select Odd!')
                    else:
                        sockets[guest].send('Opponent Select Even!')
                    if num[guest] != -1: #判断对方是否已选好
                        #计算结果并通知对手有结果了
                        isWaiting = False
                        result[address] = num[address] + num[guest]
                        result[guest] = result[address]
                        print address, 'guess', num[address], guest, 'guess', num[guest]
                        guessCondition[guest].acquire()
                        guessCondition[guest].notify()
                        guessCondition[guest].release()
                    else:
                        #等待结果
                        isWaiting = True
                        myGuessCondition.acquire()
                    lock.release()
                    
                    if isWaiting: #等待对手选择
                        myGuessCondition.wait()
                        myGuessCondition.release()
                    
                    #5秒后发送结果
                    time.sleep(5)
                    
                    try:
                        if result[address] % 2 == 0:
                            sock.send('Even!')
                        elif result[address] == -1:
                            sock.send('Opponent Quit!')
                        else:
                            sock.send('Odd!')
                    except:
                        print '>>>A \'send\' error happened! IP:', address[0], 'PORT:', address[1]
                        break
                        
                else: #玩家是Guest
                    inGame = False
                    for host in game.keys():
                        if cmp(game[host], address) == 0: #查找对手
                            #告诉对手自己的选择
                            if msgs[1] == '1\n':
                                sockets[host].send('Opponent Select Odd!')
                            else:
                                sockets[host].send('Opponent Select Even!')
                            inGame = True
                            if num[host] != -1:
                                #计算结果并通知对手有结果了
                                isWaiting = False
                                result[address] = num[address] + num[host]
                                result[host] = result[address]
                                print host, 'guess', num[host], address, 'guess', num[address]
                                guessCondition[host].acquire()
                                guessCondition[host].notify()
                                guessCondition[host].release()
                            else:
                                #等待结果
                                isWaiting = True
                                myGuessCondition.acquire()
                            lock.release()
                    
                            if isWaiting: #等待对手选择
                                myGuessCondition.wait()
                                myGuessCondition.release()
                            
                            #5秒后发送结果
                            time.sleep(5)
                            
                            try:
                                if result[address] % 2 == 0:
                                    sock.send('Even!')
                                elif result[address] == -1:
                                    sock.send('Opponent Quit!')
                                else:
                                    sock.send('Odd!')
                            except:
                                print '>>>A \'send\' error happened! IP:', address[0], 'PORT:', address[1]
                                break
                            break
                    
                    if not inGame: #发现对手已经退出，通知玩家
                        lock.release()
                        try:
                            sock.send('Opponent Quit!')
                        except:
                            print '>>>A \'send\' error happened! IP:', address[0], 'PORT:', address[1]
                            break
                
                #将选择的数字设为无效值
                lock.acquire()
                num[address] = -1
                lock.release()
                
        except:
            print '>>>A \'send\' error happened! IP:', address[0], 'PORT:', address[1]
            break

    #退出游戏，并解除对手等待状态，此处主要是为了连接不正常断开的情况做清除处理
    quitGame(address)

    #清理全局变量
    lock.acquire()
    if address in num:
        del num[address]
    if address in gameCondition:
        del gameCondition[address]
    if address in guessCondition:
        del guessCondition[address]
    if address in result:
        del result[address]
    clientCount -= 1
    lock.release()
    
    print '>>>Disconnected with', address[0], 'at port', address[1]
    print clientCount, waiting, game
    print num
    sock.close()

def listenThread(tcpSerSock):
    '''
    Function called by thread to listen a connection
    '''

    global clientCount

    while True:

        if clientCount >= MAX_CLIENTS:
            time.sleep(5)
            continue

        try:
            conn, addr = tcpSerSock.accept()
            print 'Connected from:', addr
        except:
            print '>>>An \'accept\' error happened!'
            continue

        #创建新线程处理连接
        Thread(target=connectThread, args=(conn, addr)).start()
        
    tcpSerSock.close()

def runServer(HOST, PORT):
    '''
    Run TCP server
    '''
    
    ADDR = (HOST, PORT)

    try:
        tcpSerSock = socket(AF_INET, SOCK_STREAM)
    except:
        print 'Fail to open socket!'
        sys.exit(1)

    try:
        tcpSerSock.bind(ADDR)
    except:
        print 'Fail to bind port!'
        tcpSerSock.close()
        sys.exit(1)

    try:
        tcpSerSock.listen(MAX_CLIENTS)
    except:
        print 'Fail to listen!'
        tcpSerSock.close()
        sys.exit(1)

    print 'Waiting for connection…'
    
    #创建新线程开始监听
    Thread(target=listenThread, args=(tcpSerSock,)).start()

if __name__ == '__main__':

    HOST = 'localhost'
    PORT = 21567
    runServer(HOST, PORT)